package com.zh.work.aop;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.zh.common.entity.JSONResult;
import com.zh.common.entity.Oauth;
import com.zh.common.util.CheckObjAllFieldsIsNullUtil;
import com.zh.common.util.IpAddressUtil;
import com.zh.work.dao.platform.agent.AgentInfoMapper;
import com.zh.work.dao.platform.branch.BranchInfoMapper;
import com.zh.work.dao.platform.merch.MerchInfoMapper;
import com.zh.work.dao.platform.virtual.VirtualCardMapper;
import com.zh.work.entity.VirtualRecordEntity;
import com.zh.work.entity.agent.AgentInfoEntity;
import com.zh.work.entity.branch.BranchInfoEntity;
import com.zh.work.entity.merch.MerchInfoEntity;
import com.zh.work.entity.virtual.*;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * code is far away from bug with the animal protecting
 * ┏┓　　　┏┓
 * ┏┛┻━━━┛┻┓
 * ┃　　　　　　　┃
 * ┃　　　━　　　┃
 * ┃　┳┛　┗┳　┃
 * ┃　　　　　　　┃
 * ┃　　　┻　　　┃
 * ┃　　　　　　　┃
 * ┗━┓　　　┏━┛
 * 　　┃　　　┃神兽保佑
 * 　　┃　　　┃代码无BUG！
 * 　　┃　　　┗━━━┓
 * 　　┃　　　　　　　┣┓
 * 　　┃　　　　　　　┏┛
 * 　　┗┓┓┏━┳┓┏┛
 * 　　　┃┫┫　┃┫┫
 * 　　　┗┻┛　┗┻┛
 *
 * @Description: 账户AOP日志记录
 * @Author: Li.HuiNeng
 * @Create in: 2020-02-10 14:34
 * @Version: 1.0.1
 */
//@Aspect
//@Component
public class VirtualRecordAop {

    @Autowired
    private VirtualCardMapper cardMapper;

    @Autowired
    private BranchInfoMapper branchMapper;

    @Autowired
    private AgentInfoMapper agentMapper;

    @Autowired
    private MerchInfoMapper merchMapper;

    private static final Logger log = LoggerFactory.getLogger(VirtualRecordAop.class);

    // 获取当前日期
    private String DATE = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
    // 获取时间
    private String TIME = new SimpleDateFormat("HH:mm:ss").format(new Date());
    // 格式化
    public static SimpleDateFormat TRACENO_DATE_TIME_FORMAT = new SimpleDateFormat("yyyyMMddHHmmss");

    // 实体信息
    private static final String SYSTEM_HOST_ENTITY = "SystemHostEntity";
    private static final String VIRTUAL_CARD_ENTITY = "VirtualCardEntity";
    private static final String VIRTUAL_CHECK_ENTITY = "VirtualCheckEntity";
    private static final String VIRTUAL_RATE_ENTITY = "VirtualRateEntity";
    private static final String VIRTUAL_RATE_TYPE_ENTITY = "VirtualRateTypeEntity";
    private static final String VIRTUAL_RECHARGE_TRANS = "VirtualRechargeTrans";


    /**
     * 记录充值流水
     */
    @Pointcut("execution(* com.zh.work.controller.platform.virtual..putAddVirtualRechargeTrans(..))")
    public void addRechargeTransLog() {
    }


    @Async
    @AfterReturning(value = "addRechargeTransLog()", returning = "result")
    public void addAroundLog(@NotNull JoinPoint jp, JSONResult result) {

        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String ip = IpAddressUtil.getIpAddress(request);
        // 获取当前操作人对象
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Oauth auth = (Oauth) authentication.getPrincipal();
        // 排除为空参数之后保留的参数
        JSONObject json = new JSONObject();
        try {
            json = CheckObjAllFieldsIsNullUtil.getReflect(jp.getArgs()[0]);
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        VirtualRechargeTrans rechargeTrans = new VirtualRechargeTrans();

        MethodSignature methodSignature = (MethodSignature) jp.getSignature();
        Method method = methodSignature.getMethod();
        try {
            // 查询指定账户信息
            VirtualCardEntity cardEntity = cardMapper.selectOne(new LambdaQueryWrapper<VirtualCardEntity>().eq(VirtualCardEntity::getCardNo, json.get("CardNo")));
            //获取ApiOperation注解设置的notes描述内容作为信息
            if (method.isAnnotationPresent(ApiOperation.class)) {
                ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
                //方法完整路径,方法名,备注信息
                log.info("请求记录:{}.{}==>[ {} ]", jp.getTarget().getClass().getName(), method.getName(), apiOperation.notes());
                rechargeTrans.setTransDate(DATE);
                rechargeTrans.setTransTime(TIME);
                rechargeTrans.setBranchNo(cardEntity.getBranchNo());
                // 查询机构信息
                BranchInfoEntity branchInfoEntity = branchMapper.selectOne(new LambdaQueryWrapper<BranchInfoEntity>()
                        .select(BranchInfoEntity::getBranchName)
                        .eq(BranchInfoEntity::getBranchNo, cardEntity.getBranchNo()));
                rechargeTrans.setBranchName(branchInfoEntity.getBranchName());
                rechargeTrans.setCardNo(cardEntity.getCardNo());
                rechargeTrans.setTransType(1);
                rechargeTrans.setStatus(2);
                // 查询商户信息
                MerchInfoEntity merch = merchMapper.selectOne(new LambdaQueryWrapper<MerchInfoEntity>().eq(MerchInfoEntity::getMerchNo, cardEntity.getMerchNo()));
                if (merch != null) {
                    rechargeTrans.setBranchNo(merch.getBranchNo());
                    rechargeTrans.setBranchName(branchInfoEntity.getBranchName());
                    rechargeTrans.setAgentNo(merch.getSuperAgent());
                    // 查询代理商信息
                    AgentInfoEntity agentBean = agentMapper.selectOne(new LambdaQueryWrapper<AgentInfoEntity>().eq(AgentInfoEntity::getAgentNo, merch.getSuperAgent()));
                    if (agentBean == null) {
                        rechargeTrans.setAgentName(branchInfoEntity.getBranchName());
                    } else {
                        rechargeTrans.setAgentName(agentBean.getAgentName());
                    }
                    rechargeTrans.setMerchNo(merch.getMerchNo());
                    rechargeTrans.setMerchName(merch.getMerchName());
                }
                rechargeTrans.setAmount(Float.valueOf(json.get("Amount").toString()));
                rechargeTrans.setTransAmount(Float.valueOf(json.get("Amount").toString()));
                rechargeTrans.setTransFee(0F);
                rechargeTrans.setTraceNo("0000" + TRACENO_DATE_TIME_FORMAT.format(new Date()));
                rechargeTrans.setRemark(json.get("Remark").toString());
                String transRefNo = TRACENO_DATE_TIME_FORMAT.format(new Date());
                rechargeTrans.setTransRefNo(transRefNo);
                rechargeTrans.setAddTime(DATE + " " + TIME);
                if (auth.getUserLoginName() != null) {
                    rechargeTrans.setLoginName(auth.getUserLoginName());
                } else {
                    rechargeTrans.setLoginName("manager");
                }
                rechargeTrans.setTypeCode("0001");
                Float amount = Float.valueOf(json.get("Amount").toString());
                // 充值前金额，由于用切面，先进行的修改，所以减去充值金额则是充值前金额
                rechargeTrans.setAmountBefore(cardEntity.getAvailAmount() - amount);
                // 充值后金额
                rechargeTrans.setAmountAfter(cardEntity.getAvailAmount());
                try {
                    rechargeTrans.insert();
                } catch (Exception e) {
                    log.error("插入历史记录失败,错误原因:{}", e.getLocalizedMessage());
                }
            }

        } catch (Exception e) {
            log.error("异常信息:{}", e.getLocalizedMessage());
        }

    }


    @Pointcut("execution(* com.zh.work.controller.platform.virtual..putMod*(..)) " +
            "|| execution(* com.zh.work.controller.platform.virtual..delete*(..))")
    public void recordControllerLog() {
    }

    @Pointcut("execution(* com.zh.work.controller.platform.virtual..putAdd*(..))")
    public void addControllerLog() {
    }


    /**
     * redis实例
     */
    @Autowired
    private RedisTemplate redisTemplate;

    @Qualifier("taskExecutor")
    @Autowired
    private TaskExecutor taskExecutor;

    @SuppressWarnings("all")
    @Async
    @Before(value = "recordControllerLog()")
    public void beforeLog(@NotNull JoinPoint jp) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        VirtualRecordEntity virtualRecordEntity = new VirtualRecordEntity();
        // 获取ip
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String ip = IpAddressUtil.getIpAddress(request);
        // 获取方法信息
        MethodSignature methodSignature = (MethodSignature) jp.getSignature();
        Method method = methodSignature.getMethod();
        // 获取当前操作人对象
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Oauth auth = (Oauth) authentication.getPrincipal();
        /* 获取ApiOperation注解设置的notes描述内容作为信息 */
        ApiOperation apiOperation = null;
        if (method.isAnnotationPresent(ApiOperation.class)) {
            apiOperation = method.getAnnotation(ApiOperation.class);
        }
        JSONObject json;
        Long id = null;
        if (jp.getArgs() == null) {
            log.warn("JoinPoint中传入目标方法的参数对象为空!");
        }
        int i = 0;
        if (!(jp.getArgs()[0] instanceof ArrayList)) {
            json = CheckObjAllFieldsIsNullUtil.getReflect(jp.getArgs()[0]);
            Assert.notNull(json.get("Id"),"修改前置增强,主键为空!");
            id = Long.parseLong(json.get("Id").toString());
            // 获取类名，并选择对应的类查询，添加记录
            String className = jp.getArgs()[0].getClass().getSimpleName();
            // 获取不同实体类修改前值,存入缓存
            try {
                if (id != null && !"".equals(id.toString())) {
                    switch (className) {
                        case SYSTEM_HOST_ENTITY:
                            SystemHostEntity systemHostEntity = new SystemHostEntity();
                            systemHostEntity = (SystemHostEntity) systemHostEntity.selectById(id);
                            if (systemHostEntity != null) {
                                i = 1;
                                this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, systemHostEntity, 60, TimeUnit.DAYS);
                            }
                            assert systemHostEntity != null;
                            virtualRecordEntity.setBeforeValue(JSON.toJSONString(systemHostEntity));
                            break;
                        case VIRTUAL_CARD_ENTITY:
                            VirtualCardEntity virtualCardEntity = new VirtualCardEntity();
                            virtualCardEntity = (VirtualCardEntity) virtualCardEntity.selectById(id);
                            if (virtualCardEntity != null) {
                                i = 1;
                                this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, virtualCardEntity, 60, TimeUnit.DAYS);
                            }
                            assert virtualCardEntity != null;
                            virtualRecordEntity.setBeforeValue(JSON.toJSONString(virtualCardEntity));
                            break;
                        case VIRTUAL_CHECK_ENTITY:
                            VirtualCheckEntity virtualCheckEntity = new VirtualCheckEntity();
                            virtualCheckEntity = (VirtualCheckEntity) virtualCheckEntity.selectById(id);
                            if (virtualCheckEntity != null) {
                                i = 1;
                                this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, virtualCheckEntity, 60, TimeUnit.DAYS);
                            }
                            assert virtualCheckEntity != null;
                            virtualRecordEntity.setBeforeValue(JSON.toJSONString(virtualCheckEntity));
                            break;
                        case VIRTUAL_RATE_ENTITY:
                            VirtualRateEntity virtualRateEntity = new VirtualRateEntity();
                            virtualRateEntity = (VirtualRateEntity) virtualRateEntity.selectById(id);
                            if (virtualRateEntity != null) {
                                i = 1;
                                this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, virtualRateEntity, 60, TimeUnit.DAYS);
                            }
                            assert virtualRateEntity != null;
                            virtualRecordEntity.setBeforeValue(JSON.toJSONString(virtualRateEntity));
                            break;
                        case VIRTUAL_RATE_TYPE_ENTITY:
                            VirtualRateTypeEntity virtualRateTypeEntity = new VirtualRateTypeEntity();
                            virtualRateTypeEntity = (VirtualRateTypeEntity) virtualRateTypeEntity.selectById(id);
                            if (virtualRateTypeEntity != null) {
                                i = 1;
                                this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, virtualRateTypeEntity, 60, TimeUnit.DAYS);
                            }
                            assert virtualRateTypeEntity != null;
                            virtualRecordEntity.setBeforeValue(JSON.toJSONString(virtualRateTypeEntity));
                            break;
                        case VIRTUAL_RECHARGE_TRANS:
                            VirtualRechargeTrans virtualRechargeTrans = new VirtualRechargeTrans();
                            virtualRechargeTrans = (VirtualRechargeTrans) virtualRechargeTrans.selectById(id);
                            if (virtualRechargeTrans != null) {
                                i = 1;
                                this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, virtualRechargeTrans, 60, TimeUnit.DAYS);
                            }
                            assert virtualRechargeTrans != null;
                            virtualRecordEntity.setBeforeValue(JSON.toJSONString(virtualRechargeTrans));
                            break;

                        default:
                            log.warn("未找到对应数据类型,修改前数据未存入缓存,请尽快检查问题。");
                    }
                    virtualRecordEntity.setAfterValue(json.toJSONString());
                    virtualRecordEntity.setRequestParam(json.toJSONString());
                } else {
                    // 用于记录最终修改值
                    StringBuffer sb = new StringBuffer();
                    ArrayList arrayList = (ArrayList) jp.getArgs()[0];
                    arrayList.forEach(data -> {
                        sb.append(JSON.toJSONString(data) + "#");
                    });
                    sb.substring(0, sb.length() - 1);
                    virtualRecordEntity.setRequestParam(sb.toString());
                }
                taskExecutor.execute(() -> {
                    virtualRecordEntity.setRequestFunc(jp.getTarget().getClass().getName() + method.getName());
                    virtualRecordEntity.setCreateTime(DATE);
                    virtualRecordEntity.setIp(ip);
                    virtualRecordEntity.setOperatorId(auth.getId());
                    virtualRecordEntity.setSuccess(true);
                    try {
                        virtualRecordEntity.insert();
                    } catch (Exception e) {
                        log.error("插入历史记录失败,错误原因:{}", e.getLocalizedMessage());
                    }
                });
                assert apiOperation != null;
                log.info("请求记录:{}.{}==>[ {} ]", jp.getTarget().getClass().getName(), method.getName(), apiOperation.notes());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    @SuppressWarnings("all")
    @Async
    @AfterReturning(value = "addControllerLog()", returning = "result")
    public void addAfterLog(@NotNull JoinPoint jp, JSONResult result) {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String ip = IpAddressUtil.getIpAddress(request);
        // 获取当前操作人对象
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Oauth auth = (Oauth) authentication.getPrincipal();
        // 排除为空参数之后保留的参数
        JSONObject json = new JSONObject();
        try {
            json = CheckObjAllFieldsIsNullUtil.getReflect(jp.getArgs()[0]);
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        VirtualRecordEntity recordEntity = new VirtualRecordEntity();
        MethodSignature methodSignature = (MethodSignature) jp.getSignature();
        Method method = methodSignature.getMethod();
        try {
            //获取ApiOperation注解设置的notes描述内容作为信息
            if (method.isAnnotationPresent(ApiOperation.class)) {
                ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
                //方法完整路径,方法名,备注信息
                log.info("请求记录:{}.{}==>[ {} ]", jp.getTarget().getClass().getName(), method.getName(), apiOperation.notes());
                recordEntity.setRequestFunc(jp.getTarget().getClass().getName() + method.getName());
                recordEntity.setCreateTime(DATE);
                recordEntity.setIp(ip);
                recordEntity.setOperatorId(auth.getId());
                recordEntity.setRequestParam(json.toJSONString());
                recordEntity.setSuccess(result.getSuccess());
                recordEntity.setRemark(StringUtils.isNotEmpty(result.getMessage()) ? result.getMessage() : "");
                try {
                    recordEntity.insert();
                } catch (Exception e) {
                    log.error("插入历史记录失败,错误原因:{}", e.getLocalizedMessage());
                }
            }
            // 将当前操作人登录名+当前时间(时分秒)作为key存入redis中,60天失效期
            redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, json, 60, TimeUnit.DAYS);
        } catch (Exception e) {
            log.error("异常信息:{}", e.getLocalizedMessage());
        }
    }


}
